package cn.hm.search.service.impl;

import cn.hm.common.response.Result;
import cn.hm.goods.api.feign.SkuFeign;
import cn.hm.goods.api.po.Sku;
import cn.hm.search.api.pojo.SkuInfo;
import cn.hm.search.dao.SkuEsMapper;
import cn.hm.search.service.SkuEsService;
import com.alibaba.fastjson.JSON;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * @author Mxb
 * @version 1.0
 * @date 2020/10/3 15:17
 */
@Service
public class SkuEsServiceImpl implements SkuEsService {

    @Autowired
    private SkuFeign skuFeign;

    /**
     * 注入Es ，可以实现索引库的增删改查[高级搜索]
     */
    @Autowired
    private ElasticsearchTemplate esTemplate;

    @Autowired
    private SkuEsMapper skuEsMapper;

    /**
     * 导入索引库
     */
    @Override
    public void importData() {
        //Feign调用，查询到List<Sku> 集合
        Result<List<Sku>> listSku = skuFeign.findAll();
        // 将List<Sku> 转成List<SkuInfo>
        //JSONObject.parseArray(listSku, SkuInfo.class);
        List<SkuInfo> skuInfoList = JSON.parseArray(JSON.toJSONString(listSku.getData()), SkuInfo.class);

        // 生成规格动态域
        for (SkuInfo skuInfo : skuInfoList) {
            //获取到spec  --> Map<String>  -->Map类型
            //{'颜色': '红色', '版本': '8GB+128GB'}
            Map<String, Object> map = JSON.parseObject(skuInfo.getSpec(), Map.class);

            //如果要生成动态的域，只需要将该域存入到一个Map<String,Object>对象中即可，该Map<String,Object>的key
            //会生成一个域，域的名字为该Map的key
            //当前Map<String,Object>后面的Object的值会作为当前sku对象该域（key）对应的值
            skuInfo.setSpecMap(map);
        }


        //调用Dao实现数据批量导入
        skuEsMapper.saveAll(skuInfoList);
    }

    /**
     * 首页搜索框  实现方法
     * 多条件搜索
     * @param searchMap
     * @return
     */
    @Override
    public Map<String, Object> search(Map<String, String> searchMap) {
        //2.创建查询对象 的构建对象
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //添加多添加组合查询
        // must must_not should
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();


        // 不为空，切大于0，才获取
        if (searchMap != null && searchMap.size() > 0) {
            // 获取关键字的值
            String keywords = searchMap.get("keywords");
            // 如果关键词不为空，则搜索关键词数据
            if (!StringUtils.isEmpty(keywords)) {
                //3.设置查询的条件
                //nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("name", keywords));
                //设置主关键字查询
                //nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(keywords, "name", "brandName", "categoryName"));
                // 根据关键字查询
                //nativeSearchQueryBuilder.withQuery(QueryBuilders.queryStringQuery(keywords).field("name"));
                boolQueryBuilder.must(QueryBuilders.queryStringQuery(keywords).field("name"));
            }
            //品牌条件  品牌过滤条件不为空
            //输入了品牌
            if (!StringUtils.isEmpty(searchMap.get("brand"))) {
                // 获取品牌名字
                String brand = searchMap.get("brand");
                boolQueryBuilder.filter(QueryBuilders.termQuery("brandName", brand));
            }
            //分类条件 分类过滤条件不为空
            //输入了分类
            if (!StringUtils.isEmpty(searchMap.get("category"))) {
                String category = searchMap.get("category");
                boolQueryBuilder.filter(QueryBuilders.termQuery("categoryName", category));
            }
        }
        //设置高亮条件
        nativeSearchQueryBuilder.withHighlightFields(new HighlightBuilder.Field("name"));
        nativeSearchQueryBuilder.withHighlightBuilder(new HighlightBuilder().preTags("<em style=\"color:red\">").postTags("</em>"));

        //设置分组条件  商品分类
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuCategorygroup").field("categoryName").size(50));
        //设置分组条件  商品品牌
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrandgroup").field("brandName").size(50));
        //设置分组条件  商品的规格
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpecgroup").field("spec.keyword").size(10000));


        //规格过滤查询
        if (searchMap != null) {
            for (String key : searchMap.keySet()) {
                if (key.startsWith("spec_")) {
                    boolQueryBuilder.filter(QueryBuilders.termQuery("specMap." + key.substring(5) + ".keyword", searchMap.get(key)));
                }
            }
        }
        //价格过滤查询
        String price = searchMap.get("price");
        if (!StringUtils.isEmpty(price)) {
            String[] split = price.split("-");
            if (!split[1].equalsIgnoreCase("*")) {
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").from(split[0], true).to(split[1], true));
            } else {
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(split[0]));
            }
        }

        //构建过滤查询
        nativeSearchQueryBuilder.withFilter(boolQueryBuilder);


        //构建分页查询 start
        Integer pageNum = 1;
        if (!StringUtils.isEmpty(searchMap.get("pageNum"))) {
            try {
                pageNum = Integer.valueOf(searchMap.get("pageNum"));
            } catch (NumberFormatException e) {
                e.printStackTrace();
                pageNum = 1;
            }
        }
        Integer pageSize = 3;
        nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum - 1, pageSize));
        //构建分页查询 end

        //构建排序查询 start
        String sortRule = searchMap.get("sortRule");
        String sortField = searchMap.get("sortField");
        if (!StringUtils.isEmpty(sortRule) && !StringUtils.isEmpty(sortField)) {
            nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(sortRule.equals("DESC") ? SortOrder.DESC : SortOrder.ASC));
        }
        //构建排序查询 end

        //4.构建查询对象
        NativeSearchQuery query = nativeSearchQueryBuilder.build();


        /**
         * 5.执行查询
         * 执行搜索，响应结果给我
         * 第一个参数：搜索条件封装对象
         * 第二个参数：搜索的条件结果集（集合数据）需要转换的类型
         */
        AggregatedPage<SkuInfo> skuPage = esTemplate.queryForPage(query, SkuInfo.class, new SearchResultMapperImpl());


        //获取分组结果
        StringTerms stringTermsCategory = (StringTerms) skuPage.getAggregation("skuCategorygroup");
        //获取分组结果  商品品牌
        StringTerms stringTermsBrand = (StringTerms) skuPage.getAggregation("skuBrandgroup");
        //获取分组结果  商品规格数据
        StringTerms stringTermsSpec = (StringTerms) skuPage.getAggregation("skuSpecgroup");

        // 获得分类信息 商品分类
        List<String> categoryList = getStringsCategoryList(stringTermsCategory);
        //调用私有方法  商品品牌
        List<String> brandList = getStringsBrandList(stringTermsBrand);
        //调用私有方法  商品规格
        Map<String, Set<String>> specMap = getStringSetMap(stringTermsSpec);


        //6.创建Map，构造返回结果
        Map resultMap = new HashMap<>();
        //返回品牌列表
        resultMap.put("brandList", brandList);
        //返回分类列表
        resultMap.put("categoryList", categoryList);
        //返回规格列表
        resultMap.put("specMap", specMap);

        resultMap.put("rows", skuPage.getContent());
        resultMap.put("total", skuPage.getTotalElements());
        resultMap.put("totalPages", skuPage.getTotalPages());

        return resultMap;
    }

    /**
     * 获取品牌列表
     * @param stringTermsBrand
     * @return
     */
    private List<String> getStringsBrandList(StringTerms stringTermsBrand) {
        List<String> brandList = new ArrayList<>();
        if (stringTermsBrand != null) {
            for (StringTerms.Bucket bucket : stringTermsBrand.getBuckets()) {
                brandList.add(bucket.getKeyAsString());
            }
        }
        return brandList;
    }

    /**
     * 获取分类列表数据
     * 分类 分组查询
     * @param stringTerms
     * @return
     */
    private List<String> getStringsCategoryList(StringTerms stringTerms) {
        List<String> categoryList = new ArrayList<>();
        if (stringTerms != null) {
            for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
                //分组的值
                String keyAsString = bucket.getKeyAsString();
                categoryList.add(keyAsString);
            }
        }
        return categoryList;
    }

    /**
     * 获取规格列表数据
     * @param stringTermsSpec
     * @return
     */
    private Map<String, Set<String>> getStringSetMap(StringTerms stringTermsSpec) {
        // 存放 不重复的规格 参数
        Map<String, Set<String>> specMap = new HashMap<String, Set<String>>();
        // 存放不重复的 集合
        Set<String> specList = new HashSet<>();
        if (stringTermsSpec != null) {
            //
            for (StringTerms.Bucket bucket : stringTermsSpec.getBuckets()) {
                specList.add(bucket.getKeyAsString());
            }
        }
        // list集合中 嵌套map   遍历集合
        for (String specjson : specList) {
            // specjson json串  转换为map类型
            Map<String, String> map = JSON.parseObject(specjson, Map.class);

            for (Map.Entry<String, String> entry : map.entrySet()) {//
                String key = entry.getKey();        //规格名字
                String value = entry.getValue();    //规格选项值
                //获取当前规格名字对应的规格数据
                Set<String> specValues = specMap.get(key);
                if (specValues == null) {
                    specValues = new HashSet<String>();
                }
                //将当前规格加入到集合中
                specValues.add(value);
                //将数据存入到specMap中
                specMap.put(key, specValues);
            }
        }
        return specMap;
    }
}
